/** bcmi_frame mpi reader definition.
 *
 *  bcmi_frame mpi reader definition.
 *
 *  @author : huicong
 *  @date : 2008-10-27
 *
 */

#include "MPIReader.h"

#include "BasicHeader.h"
#include "BCMI_Frame.h"
#include "ParallelFrame.h"

#include <cstdlib>
#include <cstdio>
#include <string.h>
#include <iostream>

namespace bcmi_frame
{

    VOID MPIReader::initialize()
    {
#ifdef LOG_MPI_READER
        _myLog->log(
            H_OK,
            bcmi_frame::MPI,
            __FUNCSIG__,
            "MPIReader constructor"
            );
#endif //#ifdef LOG_MPI_READER

        HRESULT hr = newArray(&_bufferPool, _maxPoolNumber); 

        if (FAILED(hr))
        {
#ifdef LOG_MPI_READER
            _myLog->log(
                hr,
                bcmi_frame::MEMORY,
                __FUNCSIG__,
                "MPIReader constructor require memory failed"
                );
#endif //#ifdef LOG_MPI_READER
            //UNDO
        }
    }

    MPIReader::MPIReader(IN UINT _rid, INOUT LogRecorder * _log): _remotePid(_rid), _myLog(_log)
    {
        initialize();
    }

    MPIReader::MPIReader(IN UINT _rid, _IN ParallelFrame *_frame):
    _remotePid(_rid),
        _myLog(_frame->getLogRecorder())
    {
        _maxPoolNumber = _frame->getMPITransferBufferPoolNumber();
        _maxPoolSize = _frame->getMPITransferBufferPoolSize();
        initialize();
    }

    MPIReader::~MPIReader()
    {
        for (UINT i = 0; i < _poolNumber; ++i)
        {
            delete[] _bufferPool[i];
            _bufferPool[i] = NULL;
        }
        _poolNumber = 0;
        _poolId = -1;
        _poolPtr = -1;
        delete[] _bufferPool;
        _bufferPool = NULL;

#ifdef LOG_MPI_READER
        _myLog->log(
            H_OK,
            bcmi_frame::MPI,
            __FUNCSIG__,
            "MPIReader destructor"
            );
#endif //#ifdef LOG_MPI_READER
        _myLog = NULL;
    }

    HRESULT MPIReader::getBufferPool()
    {
        HRESULT hr = H_OK;

        while (TRUE)
        {
            if (_poolNumber >= _maxPoolNumber) // buffer overflow
            {
                //Now, error when I have not write the file access.
                //UNDO Log

#ifdef LOG_MPI_READER
                _myLog->log(
                    H_E_MPI_READER_BUFFER_OVERFLOW,
                    bcmi_frame::MPI,
                    __FUNCSIG__,
                    "MPIReader get buffer pool more than the max pool number"
                    );
#endif //#ifdef LOG_MPI_READER
               return H_E_MPI_READER_BUFFER_OVERFLOW;
            }

            UINT nid = _poolNumber++;
            hr = newArray(&(_bufferPool[nid]), _maxPoolSize);
            if (FAILED(hr))
            {
#ifdef LOG_MPI_READER
                _myLog->log(
                    hr,
                    bcmi_frame::MEMORY,
                    __FUNCSIG__,
                    "MPIReader pool require memory failed"
                    );
#endif //#ifdef LOG_MPI_READER
                //UNDO malloc test
                return hr;
            }

            hr = receive(nid);

            if (FAILED(hr)) // receive from mpi failed
            {
#ifdef LOG_MPI_READER
                _myLog->log(
                    hr,
                    bcmi_frame::MPI,
                    __FUNCSIG__,
                    "MPI transfer failed"
                    );
#endif //#ifdef LOG_MPI_READER
                return hr;
            }

            /** Reset the pointer */
            _poolId = 0;
            _poolPtr = _BUFFER_HEAD_SIZE;

            parseHeadInfo(nid);

            if (_SEND_OVER == _sendFlag) // send over
            {
#ifdef LOG_MPI_READER
                _myLog->log(
                    hr,
                    bcmi_frame::MPI,
                    __FUNCSIG__,
                    "MPI transfer over"
                    );
#endif //#ifdef LOG_MPI_READER
                break;
            }
            else if (_SEND_CONTINUE != _sendFlag) // head info error
            {
#ifdef LOG_MPI_READER
                _myLog->log(
                    hr,
                    bcmi_frame::MPI,
                    __FUNCSIG__,
                    "MPI transfer error"
                    );
#endif //#ifdef LOG_MPI_READER
                return H_E_MPI_READER_HEAD_ERROR;
            }
        }

        return hr;
        //UNFINISHED
    }

    VOID MPIReader::parseHeadInfo(IN INT nid)
    {
        //UNDO parameter test
        memcpy(&_sendFlag, _bufferPool[nid], sizeof(_sendFlag));
        memcpy(&_realSize, (_bufferPool[nid] + sizeof(_sendFlag)), sizeof(_realSize));
        //UNFINISHED
    }

    HRESULT MPIReader::receive(IN INT nid)
    {
        //UNDO parameter test
         HRESULT hr = H_OK;
         MPI_Status mpi_status;
         MPI_Recv(_bufferPool[nid],
             _maxPoolSize,
             MPI_BYTE,
             _remotePid,
             bcmi_frame::FRAME_COMM_TAG,
             MPI_COMM_WORLD,
             &mpi_status);

#ifdef LOG_MPI_READER
         _myLog->log(
             hr,
             bcmi_frame::MPI,
             __FUNCSIG__,
             "MPI receive buffer"
             );
#endif //#ifdef LOG_MPI_READER

         //UNDO mpi_status analyze

#ifdef DEBUG_MPI_READER
         printf("RECV_BUFFER : nid = %d : " , nid);
         for (UINT i = 0; i < _maxPoolSize; ++i)
         {
             BYTE v = _bufferPool[nid][i];
             printf("%2x ", v);
         }
         std::cout << std::endl;
#endif //#ifdef DEBUG_MPI_READER

         return hr;
         //UNFINISHED
    }

    HRESULT MPIReader::read(OUT VOID *_buf, IN UINT _size, OUT UINT &_actualSize)
    {

#ifdef DEBUG_MPI_WRITER
            printf("_buf address : %d, _size : %d ----", _buf, _size);
            printf("poolId : %d, poolPtr : %d, _poolNumber : %d, _realSize : %d", 
                _poolId, _poolPtr, _poolNumber, _realSize);
            std::cout << std::endl;
#endif //#ifdef DEBUG_MPI_WRITER

        //UNDO parameter test
        HRESULT hr = H_OK;
        if (_poolId >= _poolNumber) // read more than reader has
        { 
            _actualSize = 0;
            //UNDO Log
            return H_W_MPI_READER_BUFFER_LACK;
        }
        if (_poolId + 1 == _poolNumber) // last pool
        {

          if (_poolPtr + _size > _realSize) // also read more than reader has
          {
              _actualSize = _realSize - _poolPtr;
              if (0 != _actualSize)
              {
                  memcpy(_buf, ((_bufferPool[_poolId]) + _poolPtr), _actualSize);
                  _poolPtr += _actualSize;
              }
              else 
              {
                  // Do nothing == copy 0 bytes
              }
              //UNDO Log          
              return H_W_MPI_READER_BUFFER_LACK;
          }
          else
          {
              _actualSize = _size;
              memcpy(_buf, ((_bufferPool[_poolId]) + _poolPtr), _actualSize);
              _poolPtr += _actualSize;
          }
        }
        else // middle pool
        {
            if (_size > _maxPoolSize - _poolPtr) // read more than this pool
            {
                _actualSize = _maxPoolSize - _poolPtr;
                memcpy(_buf, ((_bufferPool[_poolId]) + _poolPtr), _actualSize);
                _poolPtr = _BUFFER_HEAD_SIZE; // next pool initialize
                ++_poolId; // go next pool
                UINT _tmpSize;
                hr = read((((BYTE*)_buf) + _actualSize), _size - _actualSize, _tmpSize); // read next pool
                if (FAILED(hr))
                {
                    //UNDO Log
                    return hr;
                }
                _actualSize += _tmpSize;
            }
            else
            {
                _actualSize = _size;
                memcpy(_buf, ((_bufferPool[_poolId]) + _poolPtr), _actualSize);
                _poolPtr += _actualSize;
                if (_maxPoolSize <= _poolPtr) // only ==, read clean this pool and go next 
                {
                    _poolPtr = _BUFFER_HEAD_SIZE; // next pool initialize
                    ++_poolId; // go next pool if need
                }
            }
        }
        return hr;
        //UNFINISHED
    }

    UINT MPIReader::getRemotePid()
    {
       return _remotePid;
    }
};